<?php
declare(strict_types=1);
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/4/2
 * Time: 10:12
 */
namespace WebApp\lib\co\core;

use Generator;

/**
 * Class SystemCall
 * @package WebApp\lib\co\core
 */
class SystemCall
{
    protected $callback;
    public $name;

    /**
     * SystemCall constructor.
     * @param $name
     * @param callable $callback
     */
    public function __construct($name, callable $callback)
    {
        $this->callback = $callback;
        $this->name = $name;
    }

    /**
     * @param Task $task
     * @param Scheduler $scheduler
     * @return mixed
     */
    public function __invoke(Task $task, Scheduler $scheduler)
    {
        $callback = $this->callback;
        return $callback($task, $scheduler);
    }

    /**
     * @return SystemCall
     */
    public static function getTaskId(): SystemCall
    {
        return new SystemCall(__FUNCTION__, function (Task $task, Scheduler $scheduler) {
            $task->setSendValue($task->task_id);
            $scheduler->schedule($task);
        });
    }

    /**
     * @param $name
     * @param $tid
     * @param Generator $coroutine
     * @return SystemCall
     */
    public static function newTask($name, $tid, Generator $coroutine)
    {
        return new SystemCall($name, function (Task $task, Scheduler $scheduler) use ($coroutine, $tid) {
            $task->setSendValue($scheduler->newTask($coroutine, $tid));
        });
    }

    /**
     * @param $tid
     * @return SystemCall
     */
    public static function killTask($tid)
    {
        return new SystemCall(
            __FUNCTION__,
            function (Task $task, Scheduler $scheduler) use ($tid) {
                $task->setSendValue($scheduler->killTask($tid));
                $scheduler->schedule($task);
            }
        );
    }

    /**
     * @param $fp
     * @param int $sock_type
     * @return SystemCall
     */
    public static function await($fp, $sock_type = 0): SystemCall
    {
        return new SystemCall(__FUNCTION__, function (Task $task, Scheduler $scheduler) use (&$fp, $sock_type) {
            $scheduler->count++;
            $GLOBALS['SCHEDULER_COUNT']++;

            $after = swoole_timer_after(1000 * 10, function () use (&$fp, $scheduler) {
                var_dump('timer_after');
                if ($fp) {
                    fclose($fp);
                    {
                        swoole_event_del($fp);
                        $scheduler->count--;
                        $GLOBALS['SCHEDULER_COUNT']--;
                    }
                }
            });
            swoole_event_add($fp, function () use (&$fp, &$after, &$scheduler, &$task, $sock_type) {
                swoole_timer_clear($after);
                {
                    swoole_event_del($fp);
                    $scheduler->count--;
                    $GLOBALS['SCHEDULER_COUNT']--;
                }

                try {
                    if (!isset($scheduler->task_map[$task->pid])) {
                        throw new \ErrorException();
                    }

                    /** @var Task $parent_task */
                    $parent_task = $scheduler->task_map[$task->pid];

                    $str = '';

                    switch ($sock_type) {
                        default:
                        case 0:
                            while (!feof($fp)) {
                                $str .= fread($fp, 1024);
                            }
                    }

                    $parent_task->setSendValue($str);
                    $scheduler->schedule($parent_task);
//                    var_dump($scheduler->name . ":" .$scheduler->count);
                } catch (\ErrorException $e) {
                } finally {
                    fclose($fp);
                    $fp = null;
//                    swoole_event_exit();
                }
            });
        });
    }

    /**
     * @param Scheduler $s
     * @return SystemCall
     */
    public static function waitScheduler(Scheduler $s)
    {
        return new SystemCall(
            __FUNCTION__,
            function (Task $task, Scheduler $scheduler) use (&$s) {
                $s->addEvent($scheduler->name, $task->task_id);
            }
        );
    }
}
